package com.hww.acommon.utils.http;

import com.hww.acommon.utils.http.callback.OnDownloadListener;
import com.hww.common.util.FileIOUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.RandomAccessFile;
import java.net.URL;
import java.net.URLConnection;
import java.security.SecureRandom;
import java.security.cert.CertificateException;
import java.security.cert.X509Certificate;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import okhttp3.Callback;
import okhttp3.FormBody;
import okhttp3.Headers;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.ResponseBody;

public class OkHttpUtils {

    private OkHttpUtils() {
    }

    static {
        /*okHttpClient = getHttpsClient();*/
        int timeOut = 10;
        TimeUnit timeUnit = TimeUnit.SECONDS;
        okHttpClient = new OkHttpClient.Builder()
                /*连接超时*/
                .connectTimeout(timeOut, timeUnit)
                /*读取超时*/
                .readTimeout(timeOut, timeUnit)
                /*写超时*/
                .writeTimeout(timeOut, timeUnit)
                .sslSocketFactory(createSSLSocketFactory(), new TrustAllCerts())
                .hostnameVerifier(new TrustAllHostnameVerifier())
                .build();
    }


    private static OkHttpClient okHttpClient;

    public static OkHttpClient getOkHttpClient() {
        return okHttpClient;
    }

    /**
     * 下载文件到本地
     *
     * @param url      下载地址
     * @param filePath 本地文件保存的完整路径
     * @return
     */
    public static boolean downLoadFile(String url, String filePath) {
        /*传统的URLConnection下载文件方式*/
        try {
            File file = new File(filePath);
            /*判断文件夹是否存在或是否创建成功*/
            boolean orExistsDir = FileIOUtils.createOrExistsDir(file.getParent());
            if (orExistsDir) {
                /*构造URL*/
                URL mUrl = new URL(url);
                /*打开连接*/
                URLConnection urlConnection = mUrl.openConnection();
                /*获取输入流*/
                InputStream inputStream = urlConnection.getInputStream();
                /* 1K的数据缓冲*/
                byte[] bytes = new byte[1024];
                /*读取到的数据长度*/
                int len;

                FileOutputStream fileOutputStream = new FileOutputStream(file, true);
                /*开始读取流内容*/
                while ((len = inputStream.read(bytes)) != -1) {
                    fileOutputStream.write(bytes, 0, len);
                }

                /*下载完毕，关闭相关链接*/
                fileOutputStream.close();
                inputStream.close();

                return true;
            }
        } catch (Exception e) {
        }
        return false;
    }


    /**
     * get 请求
     *
     * @param url     url 请求地址
     * @param headers 请求头参数（可null）
     * @param query   URL参数（可null）
     * @return
     */
    public static String doGet(String url, Map<String, String> headers, Map<String, String> query) throws IOException {

        // 创建一个请求 Builder
        Request.Builder builder = new Request.Builder();
        // 创建一个 request
        Request request = builder.url(url).build();
        // 创建一个 HttpUrl.Builder
        HttpUrl.Builder urlBuilder = request.url().newBuilder();
        // 创建一个 Headers.Builder
        Headers.Builder headerBuilder = request.headers().newBuilder();

        if (null != headers && headers.size() > 0) {
            // 装载请求头参数
            for (Map.Entry<String, String> next : headers.entrySet()) {
                headerBuilder.add(next.getKey(), next.getValue());
            }
        }

        if (null != query && query.size() > 0) {
            // 装载请求的参数
            for (Map.Entry<String, String> next : query.entrySet()) {
                urlBuilder.addQueryParameter(next.getKey(), next.getValue());
            }
        }

        // 设置自定义的 builder
        // 因为 get 请求的参数，是在 URL 后面追加  http://xxxx:8080/user?name=xxxx?sex=1
        builder.url(urlBuilder.build()).headers(headerBuilder.build());

        Response execute = okHttpClient.newCall(builder.build()).execute();
        ResponseBody body = execute.body();
        String result = (null == body ? "null" : body.string());
        /*System.out.println("------------" + result);*/
        return result;
    }


    /**
     * 异步POST请求（JSON格式）
     *
     * @param url      请求地址
     * @param json     JSON请求体
     * @param headers  请求头（可null）
     * @param callback 回调接口
     */
    public static void doPostJsonAsync(String url, String json, Map<String, String> headers, okhttp3.Callback callback) {
        RequestBody body = RequestBody.create(json, MediaType.parse("application/json; charset=utf-8"));
        Request.Builder requestBuilder = new Request.Builder().url(url).post(body);
        addHeaders(requestBuilder, headers);
        okHttpClient.newCall(requestBuilder.build()).enqueue(callback);
    }

    /**
     * 提交JSON数据
     *
     * @param url     请求地址
     * @param json    JSON字符串
     * @param headers 请求头Map（可null）
     * @return 响应体字符串
     * @throws IOException 网络或协议错误时抛出
     */
    public static String doPostJson(String url, String json, Map<String, String> headers) throws IOException {
        RequestBody body = RequestBody.create(json, MediaType.parse("application/json; charset=utf-8"));
        Request.Builder requestBuilder = new Request.Builder().url(url).post(body);
        addHeaders(requestBuilder, headers);
        return execute(requestBuilder.build());
    }

    /**
     * 提交表单数据（x-www-form-urlencoded）
     *
     * @param url     请求地址
     * @param form    表单数据（可null）
     * @param headers 请求头（可null）
     */
    public static String doPostForm(String url, Map<String, String> form, Map<String, String> headers) throws IOException {
        FormBody.Builder formBuilder = new FormBody.Builder();
        if (form != null && form.size() > 0) {
            for (Map.Entry<String, String> next : form.entrySet()) {
                formBuilder.add(next.getKey(), next.getValue());
            }
        }
        Request.Builder requestBuilder = new Request.Builder().url(url).post(formBuilder.build());
        addHeaders(requestBuilder, headers);
        return execute(requestBuilder.build());
    }

    /**
     * 上传文件（multipart/form-data）
     *
     * @param url          请求地址
     * @param file         要上传的文件对象
     * @param filePartName 文件字段名称（服务端接收的参数名）
     * @param form         其他表单字段（可null）
     * @param headers      请求头Map（可null）
     * @return 响应体字符串
     * @throws IOException 网络或协议错误时抛出
     */
    public static String postFile(String url, File file, String filePartName, Map<String, String> form, Map<String, String> headers) throws IOException {
        MultipartBody.Builder multipartBuilder = new MultipartBody.Builder()
                .setType(MultipartBody.FORM)
                .addFormDataPart(filePartName, file.getName(), RequestBody.create(file, MediaType.parse("application/octet-stream")));

        if (form != null) {
            for (Map.Entry<String, String> entry : form.entrySet()) {
                multipartBuilder.addFormDataPart(entry.getKey(), entry.getValue());
            }
        }

        Request.Builder requestBuilder = new Request.Builder().url(url).post(multipartBuilder.build());
        addHeaders(requestBuilder, headers);
        return execute(requestBuilder.build());
    }

    /**
     * 异步提交原始文本数据
     *
     * @param url       请求地址
     * @param rawText   原始文本内容
     * @param mediaType 媒体类型（如text/plain）
     * @param headers   请求头Map（可null）
     * @param callback  异步回调接口
     */
    public static void postRawTextAsync(String url, String rawText, String mediaType,
                                        Map<String, String> headers, Callback callback) {
        RequestBody body = RequestBody.create(rawText, MediaType.parse(mediaType));

        Request.Builder builder = new Request.Builder().url(url).post(body);
        addHeaders(builder, headers);
        enqueue(builder.build(), callback);
    }

    /**
     * 执行异步请求
     */
    private static void enqueue(Request request, Callback callback) {
        okHttpClient.newCall(request).enqueue(callback);
    }

    /**
     * 执行请求并返回响应体
     *
     * @param request
     * @return
     * @throws IOException
     */
    private static String execute(Request request) throws IOException {
        try (Response response = okHttpClient.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                throw new IOException("Unexpected code: " + response);
            }
            ResponseBody body = response.body();
            return null == body ? "" : body.string();
        }
    }

    /**
     * 添加请求头
     *
     * @param builder
     * @param headers
     */
    private static void addHeaders(Request.Builder builder, Map<String, String> headers) {
        if (headers != null && headers.size() > 0) {
            for (Map.Entry<String, String> next : headers.entrySet()) {
                builder.addHeader(next.getKey(), next.getValue());
            }
        }
    }

    /**
     * 支持断点续传的文件下载
     * <p>
     * http://nas.zytech.site:8001/eam/update/update_vnc.txt
     *
     * @param url      下载地址
     * @param destFile 本地存储路径（需有写入权限）
     * @param listener 下载进度监听器（可为null）
     * @throws IOException 网络或文件操作异常
     */
    public static void downloadWithResume(String url, File destFile, OnDownloadListener listener) throws IOException {
        long downloadedBytes = 0;
        /*检查本地文件是否已部分下载*/
        if (destFile.exists() && destFile.isFile()) {
            downloadedBytes = destFile.length();
        }

        /*构建带Range头的请求*/
        Request request = new Request.Builder()
                .url(url)
                /*Range 头规范：确保 Range 头格式正确（如 bytes=起始位置-），且起始位置不超过服务器文件大小*/
                .header("Range", "bytes=" + downloadedBytes + "-")
                .build();

        /*执行请求并处理响应*/
        try (Response response = okHttpClient.newCall(request).execute()) {
            if (!response.isSuccessful()) {
                if (null != listener) {
                    int code = response.code();
                    listener.onDownloadFailed(String.format("下载失败，状态码：%s    %s", code, 416 == code ? "文件已存在，重新下载需要删除源文件" : ""));
                }
                return;
            }

            ResponseBody body = response.body();
            if (body == null) {
                if (null != listener) {
                    listener.onDownloadFailed("响应体为空");
                }
                return;
            }

            /*判断服务器是否支持断点续传（206状态码）*/
            boolean isResumeSupported = response.code() == 206;
            /*文件总大小*/
            long totalBytes = downloadedBytes + body.contentLength();

            /*使用随机访问文件写入*/
            try (RandomAccessFile file = new RandomAccessFile(destFile, "rw");
                 InputStream inputStream = body.byteStream()) {
                // 移动指针到已下载位置
                file.seek(downloadedBytes);

                byte[] buffer = new byte[4096];
                int bytesRead;
                long lastProgress = 0;

                while ((bytesRead = inputStream.read(buffer)) != -1) {
                    file.write(buffer, 0, bytesRead);
                    downloadedBytes += bytesRead;

                    /*进度回调（每秒最多触发一次）*/
                    if (listener != null && System.currentTimeMillis() - lastProgress > 1000) {
                        int progress = (int) ((downloadedBytes * 1.0f / totalBytes) * 100);
                        listener.onDownloading(progress);
                        lastProgress = System.currentTimeMillis();
                    }
                }

                /*最终进度通知*/
                if (listener != null) {
                    listener.onDownloadSuccess(destFile.getAbsolutePath(), String.format("文件下载成功，断点续传(%s)", isResumeSupported));
                }
            }
        }
    }

    private static OkHttpClient getHttpsClient() {
        OkHttpClient.Builder okhttpClient = new OkHttpClient().newBuilder();
        //信任所有服务器地址
        okhttpClient.hostnameVerifier(new HostnameVerifier() {
            @Override
            public boolean verify(String s, SSLSession sslSession) {
                //设置为true
                return true;
            }
        });
        //创建管理器
        TrustManager[] trustAllCerts = new TrustManager[]{new X509TrustManager() {
            @Override
            public void checkClientTrusted(
                    X509Certificate[] x509Certificates,
                    String s) throws CertificateException {
            }

            @Override
            public void checkServerTrusted(
                    X509Certificate[] x509Certificates,
                    String s) throws CertificateException {
            }

            @Override
            public X509Certificate[] getAcceptedIssuers() {
                return new X509Certificate[0];
                /*return new java.security.cert.X509Certificate[]{};*/
            }
        }};
        try {
            SSLContext sslContext = SSLContext.getInstance("TLS");
            sslContext.init(null, trustAllCerts, new SecureRandom());

            //为OkHttpClient设置sslSocketFactory
            okhttpClient.sslSocketFactory(sslContext.getSocketFactory());

        } catch (Exception e) {
            e.printStackTrace();
        }

        return okhttpClient.build();
    }


    public static class TrustAllCerts implements X509TrustManager {
        @Override
        public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
        }

        @Override
        public X509Certificate[] getAcceptedIssuers() {
            /*return new java.security.cert.X509Certificate[]{};*/
            return new X509Certificate[0];
        }
    }

    public static class TrustAllHostnameVerifier implements HostnameVerifier {
        @Override
        public boolean verify(String hostname, SSLSession session) {
            return true;
        }
    }

    public static SSLSocketFactory createSSLSocketFactory() {
        SSLSocketFactory ssfFactory = null;
        try {
            /*SSLContext sc = SSLContext.getInstance("SSL");*/
            SSLContext sc = SSLContext.getInstance("TLS");
            sc.init(null, new TrustManager[]{new TrustAllCerts()}, new SecureRandom());
            ssfFactory = sc.getSocketFactory();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ssfFactory;
    }

}
